﻿
namespace ComTools.Helpers
{

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;

    /// <summary>
    /// 程序集加载指定特性的相关帮助类
    /// </summary>    
    public class AttributesAssemblyHelper
    {
        /// <summary>
        /// 从类型获取相关程序集的描述信息，如果没有就使用默认值
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="defaultVal">如果没有可用的描述使用的默认值</param>
        /// <returns>程序集信息的描述</returns>
        public static string GetAssemblyInfoDescription(Type type, string defaultVal)
        {
            var resultString = defaultVal;
            try
            {

                // 获取程序集对象
                Assembly assembly = type.Assembly;

                // 查看是否定义了程序集描述.
                var isDefined = Attribute.IsDefined(assembly, typeof(AssemblyDescriptionAttribute));

                if (!isDefined)
                    return resultString;

                var adAttr = (AssemblyDescriptionAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyDescriptionAttribute));

                if (adAttr != null) resultString = adAttr.Description;
            }
            catch (Exception)
            {

                throw new Exception("获取程序集描述出错");
            }

            return resultString;
        }

        /// <summary>
        /// 获取指定对象的特性
        /// </summary>
        /// <typeparam name="T">指定要获取的特性的类型</typeparam>
        /// <param name="obj">对象</param>
        /// <returns>从指定对象获取的所有类型特性</returns>
        public static IList<T> GetClassAttributes<T>(object obj)
        {
            if (obj == null) return new List<T>();
            var result = new List<T>();
            try
            {
                var attributes = obj.GetType().GetCustomAttributes(typeof(T), false);
                result = attributes.Select(attribute => (T)attribute).ToList();
            }
            catch (Exception)
            {
                throw new Exception("获取指定对象的指定类型的特性出错");
            }
            return result;
        }


        /// <summary>
        /// 根据程序集名称，从程序集中获取指定类型的类型特性
        /// </summary>
        /// <typeparam name="T">指定的要获取的特性的类型</typeparam>
        /// <param name="assemblyName">程序集名称</param>
        /// <param name="action">每当获取到一个类型的指定特性时的回调，参数是键值对，键是具有特定特性的类型，值是指定的特性</param>
        /// <returns>键是具有特定特性的类型，值是指定的特性的键值对</returns>
        public static IList<KeyValuePair<Type, T>> GetClassAttributesFromAssembly<T>(string assemblyName, Action<KeyValuePair<Type, T>> action)
        {
            // 载入程序集信息
            Assembly assembly = Assembly.Load(assemblyName);

            // 获取程序集中所有类型信息
            var types = assembly.GetTypes();

            // 构建返回的具有类型和指定特性的键值对对象，键是具有特定特性的类型，值是指定的特性
            var widgets = new List<KeyValuePair<Type, T>>();

            foreach (var type in types)
            {
                var attributes = type.GetCustomAttributes(typeof(T), false);
                if (attributes.Length <= 0) continue;
                var pair = new KeyValuePair<Type, T>(type, (T)attributes[0]);
                widgets.Add(pair);
                action(pair);
            }
            return widgets;
        }


        /// <summary>
        /// 传入一个对象，获取该对象属性上的指定类型的特性和属性信息，如果对象为空，返回的是一个默认的字典
        /// </summary>
        /// <typeparam name="T">特性的类型</typeparam>
        /// <param name="obj">要获取的特性属性信息所在的对象</param>
        /// <returns>用字典封装的特性的属性信息，键是特性，值是标识该特性的属性信息</returns>
        public static IDictionary<string, KeyValuePair<T, PropertyInfo>> GetPropsWithAttributes<T>(object obj) where T : Attribute
        {
            // 校验对象是否有效
            if (obj == null) return new Dictionary<string, KeyValuePair<T, PropertyInfo>>();

            // 返回的属性信息对象
            Dictionary<string, KeyValuePair<T, PropertyInfo>> map = new Dictionary<string, KeyValuePair<T, PropertyInfo>>();

            // 获取对象的所有属性
            PropertyInfo[] props = obj.GetType().GetProperties();

            // 遍历属性，获取每个属性上标记的指定的特性信息，如果没有则不返回
            foreach (PropertyInfo prop in props)
            {
                object[] attrs = prop.GetCustomAttributes(typeof(T), true);
                if (attrs.Length > 0)
                    map[prop.Name] = new KeyValuePair<T, PropertyInfo>(attrs[0] as T, prop);
            }
            return map;
        }


        /// <summary>
        /// 传入一个对象，只获取具有指定特性的属性集合，如果对象为空，返回的是一个空集合
        /// </summary>
        /// <typeparam name="T">指定的特性类型</typeparam>
        /// <param name="obj">要获取属性信息的对象</param>
        /// <returns>该对象具有指定特性的属性集合</returns>
        public static IList<PropertyInfo> GetPropsOnlyWithAttributes<T>(object obj) where T : Attribute
        {
            // 校验对象
            if (obj == null) return new List<PropertyInfo>();

            // 获取所有属性
            PropertyInfo[] props = obj.GetType().GetProperties();

            return (from prop in props let attrs = prop.GetCustomAttributes(typeof(T), true) where attrs.Length > 0 select prop).ToList();
        }

        /// <summary>
        /// 传入一个对象，并指定特性的类型，获取一个键值对集合，用以获取该对象具有指定特性的属性信息
        /// </summary>
        /// <typeparam name="T">指定的特性的类型</typeparam>
        /// <param name="obj">要获取的对象</param>
        /// <returns>键值对的集合，键是特性对象，值是属性信息</returns>
        public static IList<KeyValuePair<T, PropertyInfo>> GetPropsWithAttributesList<T>(object obj) where T : Attribute
        {
            // 校验对象
            if (obj == null) return new List<KeyValuePair<T, PropertyInfo>>();

            // 获取对象的属性
            PropertyInfo[] props = obj.GetType().GetProperties();


            return (from prop in props
                    let attrs =
 prop.GetCustomAttributes(typeof(T), true)
                    where attrs.Length > 0
                    select new KeyValuePair<T, PropertyInfo>(attrs[0] as T, prop)).ToList();
        }


        /// <summary>
        /// 传入一个类型的集合，并且指定一个特性的类型，获取这个类型集合中每个类型中具有该特性的属性和对应的特性信息键值对集合
        /// </summary>
        /// <typeparam name="TPropAttrib">指定属性具有的特性类型</typeparam>
        /// <param name="types">需要获取结果的类型集合</param>
        /// <param name="action">当获取到属性和特性时的回调</param>
        /// <returns>获取到的属性的集合</returns>
        public static IList<KeyValuePair<PropertyInfo, TPropAttrib>> GetPropertiesWithAttributesOnTypes<TPropAttrib>(IList<Type>
            types, Action<Type, KeyValuePair<PropertyInfo, TPropAttrib>> action)
            where TPropAttrib : Attribute
        {
            var propertyAttributes = new List<KeyValuePair<PropertyInfo, TPropAttrib>>();

            foreach (var type in types)
            {
                // 获取属性信息
                var properties = type.GetProperties();

                foreach (var prop in properties)
                {
                    // 获取属性指定的特性信息
                    var attributes = prop.GetCustomAttributes(typeof(TPropAttrib), true);
                    if (attributes.Length <= 0) continue;
                    var pair = new KeyValuePair<PropertyInfo, TPropAttrib>(prop, attributes[0] as TPropAttrib);
                    propertyAttributes.Add(pair);
                    // 调用获取到属性和特性信息的委托
                    action(type, pair);
                }
            }
            return propertyAttributes;
        }
    }
}
